/* @flow */

import { extend } from 'shared/util'
import { detectErrors } from './error-detector'
import { createCompileToFunctionFn } from './to-function'

export function createCompilerCreator(baseCompile: Function): Function {
    // baseOptions平台相关的options
    // src \platforms \web \ compiler\options.js 中定义
    return function createCompiler(baseOptions: CompilerOptions) {
        // 接收：模板、用户传入相关的options选项
        // 将 createCompiler传入的options参数、用户传入的options参数、平台的options参数合并
        function compile(template: string, options?: CompilerOptions): CompiledResult {
            const finalOptions = Object.create(baseOptions)
            const errors = []
            const tips = []

            let warn = (msg, range, tip) => {
                ;(tip ? tips : errors).push(msg)
            }

            if (options) {
                if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) {
                    // $flow-disable-line
                    const leadingSpaceLength = template.match(/^\s*/)[0].length

                    warn = (msg, range, tip) => {
                        const data: WarningMessage = { msg }
                        if (range) {
                            if (range.start != null) {
                                data.start = range.start + leadingSpaceLength
                            }
                            if (range.end != null) {
                                data.end = range.end + leadingSpaceLength
                            }
                        }
                        ;(tip ? tips : errors).push(data)
                    }
                }
                // merge custom modules
                if (options.modules) {
                    finalOptions.modules = (baseOptions.modules || []).concat(options.modules)
                }
                // merge custom directives
                if (options.directives) {
                    finalOptions.directives = extend(
                        Object.create(baseOptions.directives || null),
                        options.directives
                    )
                }
                // copy other options
                for (const key in options) {
                    if (key !== 'modules' && key !== 'directives') {
                        finalOptions[key] = options[key]
                    }
                }
            }

            finalOptions.warn = warn

            const compiled = baseCompile(template.trim(), finalOptions)
            if (process.env.NODE_ENV !== 'production') {
                detectErrors(compiled.ast, warn)
            }
            compiled.errors = errors
            compiled.tips = tips
            return compiled
        }

        return {
            compile,
            compileToFunctions: createCompileToFunctionFn(compile)
        }
    }
}
